package org.force4spring.support;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;

import org.force4spring.SessionFactoryUtils;
import org.force4spring.WebServicesCallException;
import org.springframework.util.Assert;

import com.sforce.soap.partner.QueryResult;
import com.sforce.soap.partner.fault.ApiFault;

public class QueryResultList extends ArrayList {
	private ForceSession session;
	private QueryResult queryResult;

	public QueryResultList(ForceSession session, QueryResult queryResult,
			SObjectDynaBean[] beans) {
		this.queryResult = queryResult;
		this.session = session;
		addAll(Arrays.asList(beans));
	}

	/**
	 * @see java.util.AbstractList#get(int)
	 */
	@Override
	public Object get(int index) {
		int size = size();
		if (index < 0) {
			throw new ArrayIndexOutOfBoundsException(index);
		}

		if (index < super.size()) {
			return super.get(index);
		} else if ((index >= super.size()) && (index < size)) {
			loadNextBatch();
			return super.get(index);
		} else {
			throw new ArrayIndexOutOfBoundsException(index);
		}
	}

	private void loadNextBatch() {
		if (!session.isOpen()) {
			throw new SessionClosedException();
		}
		
		try {
			QueryResultList batch = (QueryResultList) session
					.queryMore(queryResult.getQueryLocator());
			queryResult = batch.getQueryResult();
			int count = batch.actualSize();
			for (int i=0;i<count;i++) {
				Assert.notNull(batch.get(i), "Null bean in the next batch");
				add(batch.get(i));
			}
		} catch (ApiFault fault) {
			throw SessionFactoryUtils.convertApiFault(fault);
		} catch (RemoteException re) {
			throw new WebServicesCallException(re);
		}
	}

	/**
	 * @see java.util.AbstractCollection#size()
	 */
	@Override
	public int size() {
		return queryResult.getSize();
	}

	protected QueryResult getQueryResult() {
		return queryResult;
	}
	
	protected int actualSize() {
		return super.size();
	}

	/**
	 * @see java.util.AbstractCollection#toString()
	 */
	@Override
	public String toString() {
		StringBuffer result = new StringBuffer();
		result.append("[");
		int count = this.actualSize();
		for (int i=0;i<count;i++) {
			if (i > 0) {
				result.append(", ");
			}
			result.append(get(i));
		}
		result.append("]");
		return result.toString();
	}
}
